//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
// template class AttributeT

template <typename T>
void AttributeT<T>::AddValue(Object *obj, const T &v)
{
	// Could be written more optimal...
	uint count = GetValueCount(obj);
	assert(count < GetMaxCount());
	SetValueCount(obj, count+1);
	*AccessValueT(obj, count) = v;
}

template <typename T>
void AttributeT<T>::InsertValue(Object *obj, const T &v, uint index)
{
	// Could be written more optimal...
	uint count = GetValueCount(obj);
	assert(index <= count && count < GetMaxCount());
	SetValueCount(obj, count+1);
	for (uint i = index; i < count-1; i++)
		*AccessValueT(obj, i+1) = *AccessValueT(obj, i);
	*AccessValueT(obj, index) = v;
}

template <typename T>
void AttributeT<T>::RemoveValue(Object *obj, uint index)
{
	// Could be written more optimal...
	uint count = GetValueCount(obj);
	assert(index < count && count > GetMinCount());
	for (uint i = index; i < count-1; i++)
		*AccessValueT(obj, i) = *AccessValueT(obj, i+1);
	SetValueCount(obj, count-1);
}

template <typename T>
void AttributeT<T>::SwapValues(Object *obj, uint index1, uint index2)
{
	uint count = GetValueCount(obj);
	assert(index1 < count && index2 < count);
	std::swap(*AccessValueT(obj, index1), *AccessValueT(obj, index2));
}

template <typename T>
uint AttributeT<T>::FindValue(Object *obj, const T &v)
{
	uint count = GetValueCount(obj);
	for (uint i = 0; i < count; i++)
		if (*AccessValueT(obj, i) == v)
			return i;
	return MAXUINT4;
}


//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
// template class IntegerAttribute

template <typename T>
template <typename DerivedT>
DerivedT * IntegerAttribute<T>::BaseCreate( Context &ctx, Class &cls, common::Tokenizer &tok, const string &name, uint minCount, uint maxCount, uint flags, const string &typeName )
{
	uint constraints = 0;
	T minVal = std::numeric_limits<T>::min();
	T maxVal = std::numeric_limits<T>::max();
	T step = (T)1;

	if (tok.QuerySymbol('{'))
	{
		tok.Next();

		while (!tok.QuerySymbol('}'))
		{
			tok.AssertToken(Tokenizer::TOKEN_IDENTIFIER);
			if (tok.GetString() == "Min")
			{
				tok.Next();
				tok.AssertSymbol('=');
				tok.Next();
				tok.AssertToken(Tokenizer::TOKEN_INTEGER);
				MustStrToSth<T>(&minVal, tok.GetString());
				tok.Next();
			}
			else if (tok.GetString() == "Max")
			{
				tok.Next();
				tok.AssertSymbol('=');
				tok.Next();
				tok.AssertToken(Tokenizer::TOKEN_INTEGER);
				MustStrToSth<T>(&maxVal, tok.GetString());
				tok.Next();
			}
			else if (tok.GetString() == "Step")
			{
				tok.Next();
				tok.AssertSymbol('=');
				tok.Next();
				tok.AssertToken(Tokenizer::TOKEN_INTEGER);
				MustStrToSth<T>(&step, tok.GetString());
				tok.Next();
			}
			else if (tok.GetString() == "NotZero")
			{
				tok.Next();
				constraints |= CONSTRAINT_NOT_ZERO;
			}
			else if (tok.GetString() == "Odd")
			{
				tok.Next();
				constraints |= CONSTRAINT_ODD;
			}
			else if (tok.GetString() == "Even")
			{
				tok.Next();
				constraints |= CONSTRAINT_EVEN;
			}
			else if (tok.GetString() == "Pow2")
			{
				tok.Next();
				constraints |= CONSTRAINT_POW2;
			}
			else if (tok.GetString() == "Pow2Plus1")
			{
				tok.Next();
				constraints |= CONSTRAINT_POW2_PLUS1;
			}
			else if (tok.GetString() == "Pow2Minus1")
			{
				tok.Next();
				constraints |= CONSTRAINT_POW2_MINUS1;
			}
			else
				tok.CreateError("Invalid integer attribute parameter.");

			if (!tok.QuerySymbol('}'))
			{
				tok.AssertSymbol(',');
				tok.Next();
			}
		}
		tok.Next();
	}

	DerivedT *attr = new DerivedT(name, minCount, maxCount, flags);
	attr->MinValue = minVal;
	attr->MaxValue = maxVal;
	attr->Step = step;
	attr->Constraints = constraints;
	return attr;
}

template <typename T>
IntegerAttribute<T>::IntegerAttribute(const string &name, uint minCount, uint maxCount, uint flags)
: AttributeT<T>(name, minCount, maxCount, flags)
, MinValue(std::numeric_limits<T>::min())
, MaxValue(std::numeric_limits<T>::max())
, Step((T)1)
, Constraints(0)
{
}

template <typename T>
bool IntegerAttribute<T>::ValidateValue(char *ptr, const char **outErrMsg)
{
	const T &val = *(const T*)ptr;

	if (val < MinValue)
		return false;
	if (val > MaxValue)
		return false;

	if (Constraints == 0)
		return true;
	if (Constraints & CONSTRAINT_NOT_ZERO)
		if (val == T())
			return false;
	if (Constraints & CONSTRAINT_ODD)
		if ((val & (T)1) == T())
			return false;
	if (Constraints & CONSTRAINT_EVEN)
		if ((val & (T)1) != T())
			return false;
	if (Constraints & CONSTRAINT_POW2)
		if (val < T() || !IsPow2(val))
			return false;
	if (Constraints & CONSTRAINT_POW2_PLUS1)
		if (val <= T() || !IsPow2(val-(T)1))
			return false;
	if (Constraints & CONSTRAINT_POW2_MINUS1)
		if (val < T() || !IsPow2(val+(T)1))
			return false;

	return true;
}


//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
// template class RealAttribute

template <typename T>
template <typename DerivedT>
DerivedT * RealAttribute<T>::BaseCreate( Context &ctx, Class &cls, common::Tokenizer &tok, const string &name, uint minCount, uint maxCount, uint flags, const string &typeName )
{
	uint realFlags = 0, precision = 0;
	T minVal = std::numeric_limits<T>::min();
	T maxVal = std::numeric_limits<T>::max();
	T step = (T)1.0;

	if (tok.QuerySymbol('{'))
	{
		tok.Next();

		while (!tok.QuerySymbol('}'))
		{
			tok.AssertToken(Tokenizer::TOKEN_IDENTIFIER);
			if (tok.GetString() == "Min")
			{
				tok.Next();
				tok.AssertSymbol('=');
				tok.Next();
				tok.AssertToken(Tokenizer::TOKEN_INTEGER, Tokenizer::TOKEN_FLOAT);
				MustStrToSth<T>(&minVal, tok.GetString());
				tok.Next();
				realFlags |= REAL_FLAG_USE_MIN_VALUE;
			}
			else if (tok.GetString() == "Max")
			{
				tok.Next();
				tok.AssertSymbol('=');
				tok.Next();
				tok.AssertToken(Tokenizer::TOKEN_INTEGER, Tokenizer::TOKEN_FLOAT);
				MustStrToSth<T>(&maxVal, tok.GetString());
				tok.Next();
				realFlags |= REAL_FLAG_USE_MAX_VALUE;
			}
			else if (tok.GetString() == "Step")
			{
				tok.Next();
				tok.AssertSymbol('=');
				tok.Next();
				tok.AssertToken(Tokenizer::TOKEN_INTEGER, Tokenizer::TOKEN_FLOAT);
				MustStrToSth<T>(&step, tok.GetString());
				tok.Next();
				realFlags |= REAL_FLAG_USE_STEP;
			}
			else if (tok.GetString() == "Precision")
			{
				tok.Next();
				tok.AssertSymbol('=');
				tok.Next();
				precision = tok.MustGetUint4();
				tok.Next();
			}
			else if (tok.GetString() == "Percent")
			{
				tok.Next();
				realFlags |= REAL_FLAG_PERCENT;
			}
			else if (tok.GetString() == "Radians")
			{
				tok.Next();
				realFlags |= REAL_FLAG_ANGLE_RADIANS;
			}
			else if (tok.GetString() == "Degrees")
			{
				tok.Next();
				realFlags |= REAL_FLAG_ANGLE_DEGREES;
			}
			else if (tok.GetString() == "LogScale")
			{
				tok.Next();
				realFlags |= REAL_FLAG_LOG_SCALE;
			}
			else
				tok.CreateError("Invalid real attribute parameter.");

			if (!tok.QuerySymbol('}'))
			{
				tok.AssertSymbol(',');
				tok.Next();
			}
		}
		tok.Next();
	}

	DerivedT *attr = new DerivedT(name, minCount, maxCount, flags);
	attr->MinValue = minVal;
	attr->MaxValue = maxVal;
	attr->Step = step;
	attr->Precision = precision;
	attr->RealFlags = realFlags;
	return attr;
}

template <typename T>
RealAttribute<T>::RealAttribute(const string &name, uint minCount, uint maxCount, uint flags)
: AttributeT<T>(name, minCount, maxCount, flags)
, MinValue(std::numeric_limits<T>::min())
, MaxValue(std::numeric_limits<T>::max())
, Step((T)1)
, Precision(0)
, RealFlags(0)
{
}

template <typename T>
bool RealAttribute<T>::ValidateValue(char *ptr, const char **outErrMsg)
{
	if (RealFlags == 0)
		return true;

	const T &val = *(const T*)ptr;

	if (RealFlags & REAL_FLAG_USE_MIN_VALUE)
	{
		if (val < MinValue)
		{
			if (outErrMsg)
				*outErrMsg = "Value too small.";
			return false;
		}
	}
	if (RealFlags & REAL_FLAG_USE_MAX_VALUE)
	{
		if (val > MaxValue)
		{
			if (outErrMsg)
				*outErrMsg = "Value too large.";
			return false;
		}
	}

	return true;
}
